package dbms;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Iterator;

public class Koordinator {

	private static final Koordinator _koordinator;
	static {
		try {	
			_koordinator = new Koordinator();
		}
		catch (Throwable e) {
			throw new RuntimeException(e.getMessage());
		}
	}
	
	private Hashtable<Integer, Transaction> _tas = new Hashtable<Integer, Transaction>();
	
	private Koordinator() {}
	
	static public Koordinator getInstance() {
		return _koordinator;
	}
	
	/**
	 * Beginnt eine Transaktion
	 * @return
	 */
	public int begin() {
		
		Transaction ta = new Transaction(this.getNextTaID());
		
		_tas.put(ta.getTaID(), ta);
		
		this.log("[" + this.getNextLSN()+ ",BOT_ALL,"+ta.getTaID()+"]");
		
		return ta.getTaID();
	}
	
	/**
	 * Beendet eine Transaktion
	 * @param taid
	 * @return
	 */
	public boolean commit(int taid) {
		
		boolean prepared = false; 
		boolean committed = false;
		
		
		for (RessourcenManager rm : _tas.get(taid).getRessourcenManager()) {
			
			prepared = rm.prepare(taid);
			
			if (prepared) {
				this.log("[" + this.getNextLSN() + ",RM_" + rm.getID() + " PREPARED,"+taid+"]");
			}
			else {
				this.log("[" + this.getNextLSN() + ",RM_" + rm.getID() + " ABORTED,"+taid+"]");
				this.rollback(taid);
				break;
			}
			
		}
		
		if (prepared) {
			
			this.log("[" + this.getNextLSN()+ ",COMMIT_ALL,"+taid+"]");
			
			for (RessourcenManager rm : _tas.get(taid).getRessourcenManager()) {
				
				rm.commit(taid);
			}
		}
		
		return committed;
	}
	
	/**
	 * Macht Transaktion rückgängig
	 * @param taid
	 * @return
	 */
	public boolean rollback(int taid) {
		
		boolean rollback = false;
		
		for (RessourcenManager rm : _tas.get(taid).getRessourcenManager()) {
			
			rollback = rm.rollback(taid);
		}
		
		this.log("[" + this.getNextLSN()+ ",ROLLBACK_ALL,"+taid+"]");
		
		return rollback;
	}
	
	/**
	 * Registriert RessourcenManager für eine Transaktion
	 * @param rm
	 * @param taid
	 * @return
	 */
	public boolean reg(RessourcenManager rm, int taid) {
		
		_tas.get(taid).addRessourcenManager(rm);
		
		return true;
	}
	
	/**
	 * prüft, ob die Transaktion im log committed ist
	 * @param taid
	 * @return
	 */
	public boolean isCommitted(int taid) {
		
		String logLine;
		boolean committed = false;
		
		try {
			
			RandomAccessFile logFile = new RandomAccessFile("data/log_koord.txt", "rw");
			
			 while( (logLine = logFile.readLine()) != null ) {
				 
				  if (logLine.contains("COMMIT_ALL")) {
					  
					  if ( Integer.valueOf(logLine.substring(logLine.indexOf("COMMIT_ALL") + 11, logLine.indexOf("]"))) == taid) {
						  committed = true;
					  }
				  }
			 }
		} 
		
		catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		return committed;
	}
	
	/**
	 * Schreibt Logeintrag in Log
	 * @param logEntry
	 */
	public void log(String logEntry){
		
		try {
			
			//Datei erstellen, falls nicht vorhanden
			File f = new File("data/log_koord.txt");
			if(!f.exists())
				f.createNewFile();
			
			//Daten in Seite(Datei) schreiben
			FileWriter fw = new FileWriter("data/log_koord.txt",true);
			fw.write(logEntry + System.getProperty("line.separator"));
			fw.close();
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		System.out.println(logEntry);
	}
	
	/**
	 * Gibt nächste TaID zurück und speichert diese im Config-File
	 * @return
	 */
	public synchronized int getNextTaID() {
		
		int newID = 0;
		 
		try {
			
			RandomAccessFile config = new RandomAccessFile("data/config_koord.txt", "rw");
			
			String configLine = config.readLine(); 
			
			if (configLine != null) {
				
				newID = Integer.valueOf(configLine.substring(0, configLine.indexOf(","))) + 1;
				
				configLine = configLine.substring(configLine.indexOf(","));
				configLine = newID + configLine;
				config.seek(0);
				config.write(configLine.getBytes());
			
			}
			
			else {
				config.write("0,0".getBytes());
			}
			
			config.close();
			 
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			
		} catch (IOException e) {
			e.printStackTrace();
		}

		return newID;
	}
	
	/**
	 * Gibt nächste LSN zurück und speichert diese im Config-File
	 * @return
	 */
	public synchronized int getNextLSN() {
		
		int newID = 0;
		
		try {
			
			RandomAccessFile config = new RandomAccessFile("data/config_koord.txt", "rw");
			
			String configLine = config.readLine(); 
			
			if (configLine != null) {
				
				newID = Integer.valueOf(configLine.substring(configLine.indexOf(",")+1))+1;
				
				configLine = configLine.substring(0,configLine.indexOf(",")+1);
				configLine = configLine + newID;
				config.seek(0);
				config.write(configLine.getBytes());
			
			}
			
			else {
				config.write("0,0".getBytes());
			}
			
			config.close();
			 
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			
		} catch (IOException e) {
			e.printStackTrace();
		}

		return newID;
	}
}
